vim substitute(替换)

1. substitute命令

第14章 替换

:[range]s[ubstitute]/{pattern}/{string}/[flags]
参数 说明
ranges 范围
s[ubstitute] 替换命令
匹配表达式,可以是正则表达式。,用于匹配对应文字。
替换后表达式,可以是正则表达式,用于替换后

1.1. (flags)标志位调整

标志位 说明
& 是否需要重复上次查找命令
c 在替换时确认是否替换
e 如果模式没有匹配,则不会报错
g 无需确认全部替换
i 不区分大小写
I 强制区分大小写
n 只统计匹配的数量,不进行替换
p 打印出替换成功的最后一行
# 打印出替换成功的最后一行,包括行数
l 跟p 一样只不过 ,要打印出$ 符号
r 查找模式是否重用

控制位的操作方式

答案 用途 y 替换此处匹配 n 忽略此处匹配 q 退出替换过程 l “last” —— 替换此处匹配后退出 a “all” —— 替换此处与之后所有的匹配 向上滚动屏幕 向下滚动屏幕

1.2. 重用 上次的查找命令

将substitute命令的查找域留空,意味着Vim将会重用上次的查找模式。可以利用这一特点精简工作过程。
另外需要注意一点,把查找域留空,会在命令历史中留下一项不完整的记录。由于模式通常保存在Vim的查找历史记录中,substitute命令则保存于Ex命令的历史记录中(参见 :h cmdline-history )。因此,将查找任务与替换任务分离,会致使这两组信息被单独存放,从而导致当再想重用之前的substitute命令时,会遇到困难。

如果你觉得将来会以完整形式来调用历史记录中的substitute命令,就要养成在查找域中填充内容的习惯。只需在命令行中输入 / ,即可把上次的查找内容粘贴进来。因此,通过以下命令,就可以在命令历史中创建一项完整的记录。

:%s/<C-r>//“\1”/g

在使用substitute命令时将查找域留空,有时很方便,有时却很麻烦。两种方法都体验一下,你就会形成自己的直觉,并依此来判断使用的时机。就像我常说的那样,要靠你自己的判断。

1.3. 替换域标志

magic   nomagic   action   
  &       \&      replaced with the whole matched pattern            s/\&
 \&        &      replaced with &
      \0          replaced with the whole matched pattern          \0 s/\0
      \1          replaced with the matched pattern in the first
                  pair of ()                                         s/\1
      \2          replaced with the matched pattern in the second
                  pair of ()                                         s/\2
      ..          ..                                                 s/\3
      \9          replaced with the matched pattern in the ninth
                  pair of ()                                         s/\9
  ~       \~      replaced with the {string} of the previous
                  substitute                                         s~
 \~        ~      replaced with ~                                    s/\~
      \u          next character made uppercase                      s/\u
      \U          following characters made uppercase, until \E      s/\U
      \l          next character made lowercase                      s/\l
      \L          following characters made lowercase, until \E      s/\L
      \e          end of \u, \U, \l and \L (NOTE: not <Esc>!)        s/\e
      \E          end of \u, \U, \l and \L                           s/\E
      <CR>        split line in two at this point
                  (Type the <CR> as CTRL-V <Enter>)                  s<CR>
      \r          idem                                               s/\r
      \<CR>       insert a carriage-return (CTRL-M)
                  (Type the <CR> as CTRL-V <Enter>)                  s/\<CR>
      \n          insert a <NL> (<NUL> in the file)
                  (does NOT break the line)                          s/\n
      \b          insert a <BS>                                      s/\b
      \t          insert a <Tab>                                     s/\t
      \\          insert a single backslash                          s/\\
      \x          where x is any character not mentioned above:
                  Reserved for future expansion

1.4. 用寄存器的内容替换

实际上,不必手动输入完整的替换字符串。如果某段文本已在当前文档中出现,可以先把它复制到寄存器,再通过传值或引用的方式将寄存器的内容应用至替换域。

我们已在技巧91中看到,当substitute命令的查找域为空时,Vim做出了智能的选择。人们不禁会想,如果将替换域留空的话,substitute命令也一样会重用上一次的字符串吧?但事实并非如此。将替换域留空,意味着substitute命令会用空的字符串替换每一处匹配。换句话说,所有的匹配都被删除了。

1.4.1. 传值

输入 <C-r>{register},可以将寄存器的内容插入命令行。假设我们已经复制了一些文本,如果要将它们粘贴到substitute命令的替换域,需要输入以下命令。

➾ :%s//<C-r>0/g

当输入 <C-r>0时,Vim会把寄存器0的内容粘贴进来,这意味着我们可以在执行substitute命令之前对其进行一番检查。在大多数情况下,它工作得都很好,但也引入了新的问题。

如果寄存器0中的文本包含了在替换域中具有特殊含义的字符(例如 &),就必须手动编辑这段文本,对这些字符进行转义。另外,如果寄存器0包含多行文本,有可能在命令行上显示不全。

为了避免这些问题,可以在替换域中简单地引用某个寄存器,从而得到该寄存器的内容。

1.4.2. 引用

假设已经复制了多行文本,并存放于寄存器0中。我们现在的目标是在substitute命令的替换域中使用这段文本。通过运行以下命令,可以做到这一点。

➾ :%s//\=@0/g

替换域中出现的 \= 将指示Vim执行一段表达式脚本。在Vim脚本中,可以用 @{register} 来引用某个寄存器的内容。具体来说,@0会返回复制专用寄存器的内容, @" 则返回无名寄存器的内容。因此,表达式 :%s//\=@0/g表示Vim将会用复制专用寄存器的内容替换上一次的模式。

1.4.3. 比较

先看一下这条命令。

➾ :%s/Pragmatic Vim/Practical Vim/g

再与以下命令序列进行比较。

➾ :let @/='Pragmatic Vim'
➾ :let @a='Practical Vim'
➾ :%s//\=@a/g

其中,:let @/='Pragmatic Vim' 是采用编程的方式输入查找模式,它等同于直接执行查找命令 /Pragmatic Vim<CR>(有一点不同,即运行 :let @/='Pragmatic Vim' 不会在查找历史中留下任何记录)。

同样的道理,:let @a='Practical Vim' 表示设置 a 寄存器的内容。它等同于高亮选中“Practical Vim”并用 "ay将选中的文本存入寄存器 a

这两条substitute命令都完成同一件事,即把所有“Pragmatic Vim”替换为“Practical Vim”。但要考虑它们各自的影响。

第一种方法会在命令历史中留下一项内容为 :%s/Pragmatic Vim/Practical Vim/g的记录,使人一目了然。在稍后的编辑过程中,如果我们意识到要重复这条命令,可直接从命令历史中调出该项记录,即可加以执行。总之,不会有什么意外发生。

而第二种方法会在命令历史中留下一项内容为 :%s//\=@a/g的记录。这看上去是不是相当神秘呢?

试想一下,首次运行substitute命令时,查找模式为“Pragmatic Vim”,而寄存器a包含文本“Practical Vim”。但是半小时之后,当前的查找模式可能已经被多次修改了,而且寄存器 a也可能被其他内容覆盖。因此,如果重复 :%s//\=@a/g命令,结果会与第一次执行这条命令时截然不同。

可以利用这一特点。首先,查找要操作的文本,并将替换的内容复制到寄存器a中。之后,可以重复调用命令 :%s//\=@a/g,而该命令会使用刚刚被赋值的 @/@a中的内容。接下来,可以查找新的文本,并复制新的替换字符串至寄存器 a。而当再次重复 :%s//\=@a/g命令时,运行结果将会迥然不同。

此法不妨一试。你或许会爱上它,也可能会讨厌它。但无论哪种情况,都是不错的技巧。